#include "ULCore_Prediction.h"
#include "VSDL_tools.h"

ULC_Prediction::ULC_Prediction():initFlag(false)
{
}

ULC_Prediction::ULC_Prediction(ULC_Statistic &stat)
{
	ParseStatistic(stat);
}

//Get TOP_N_RED_NUMBERS numbers from the rate table add them to random pool
#ifndef TOP_N_RED_NUMBERS
#define TOP_N_RED_NUMBERS	8
#endif
int ULC_Prediction::SetupRedPools(ULC_Statistic &stat)
{
	ULC_PositionInfo *info;
	ULC_RandomPool *pool;
	list<ULC_BallFrequency>::iterator iter;
	list<ULC_BallFrequency> tmpList;
	int count = TOP_N_RED_NUMBERS;

	if (stat.GetBlueInfo()->numberOrder.size() == 0){
		return -1;
	}

	for (int i = 0; i < UL_RED_BALLS_COUNT; i++){
		info = stat.GetSortedRedInfo(i);
		pool = redPools + i;
		info->GetHighestRates(tmpList, count);
		
		iter = tmpList.begin();

		while (count > 0 && iter != tmpList.end()){
			pool->AddNumber((*iter).number);
			iter++;
			count--;
		}
	}

#ifdef ULC_PREDICTION_DEBUG
	cout << __func__ << ":Red Balls Pool - " << redPools[0] << endl;
#endif
	return 0;
}

int ULC_Prediction::SetupOutRedPools(ULC_Statistic &stat)
{
	ULC_PositionInfo *info;
	ULC_RandomPool *pool;
	list<ULC_BallFrequency>::iterator iter;
	list<ULC_BallFrequency> tmpList;
	int count = TOP_N_RED_NUMBERS;

	if (stat.GetBlueInfo()->numberOrder.size() == 0){
		return -1;
	}

	for (int i = 0; i < UL_RED_BALLS_COUNT; i++){
		info = stat.GetOutRedInfo(i);
		pool = outRedPools + i;
		info->GetHighestRates(tmpList, count);
		
		iter = tmpList.begin();

		while (count > 0 && iter != tmpList.end()){
			pool->AddNumber((*iter).number);
			iter++;
			count--;
		}
	}

	return 0;
}

//Get TOP_N_BLUE_NUMBERS numbers with the highest rates and add them to random pool
#ifndef TOP_N_BLUE_NUMBERS
#define TOP_N_BLUE_NUMBERS	8
#endif
int ULC_Prediction::SetupBluePool(ULC_Statistic &stat)
{
	ULC_PositionInfo *info;
	list<ULC_BallFrequency>::iterator iter;
	list<ULC_BallFrequency> tmpList;
	int count = TOP_N_BLUE_NUMBERS;

	if (stat.GetBlueInfo()->numberOrder.size() == 0){
		return -1;
	}

	info = stat.GetBlueInfo();

	info->GetHighestRates(tmpList, count);
	
	iter = tmpList.begin();

	while (count > 0 && iter != tmpList.end()){
		bluePool.AddNumber((*iter).number);
		iter++;
		count--;
	}

	return 0;
}

int ULC_Prediction::ParseStatistic(ULC_Statistic &stat)
{
	initFlag = false;
	
	for (int i = 0; i < UL_RED_BALLS_COUNT; i++){
		redPools[i].Reset();
		outRedPools[i].Reset();
	}

	bluePool.Reset();

	if (SetupRedPools(stat) == -1){
		return -1;
	}

	if (SetupOutRedPools(stat) == -1){
		return -1;
	}

	if (SetupBluePool(stat) == -1){
		return -1;
	}

	initFlag = true;
	return 0;
}

int ULC_Prediction::PredictNextSerialByRandom(ULC_Serial &serial)
{
	int number;

	for (int i = 0; i < UL_RED_BALLS_COUNT; i++){
		number = VSDL_Tool::Rand() % UL_BALL_MAX_RED + 1;

		while (serial.actualOutBalls.IsRedBallExisted(number)){
			number = VSDL_Tool::Rand() % UL_BALL_MAX_RED + 1;
		}

		serial.actualOutBalls.reds[i] = number;
	}		

	serial.sortedBalls = serial.actualOutBalls;
	serial.sortedBalls.blue = VSDL_Tool::Rand() % UL_BALL_MAX_BLUE + 1;
	serial.sortedBalls.EnableOutputBlue();
	serial.sortedBalls.SortRedBalls();

	return 0;
}

int ULC_Prediction::PredictNextSerialBySorted(ULC_Serial &serial)
{
	int number;

	if (initFlag == false){
#ifdef ULC_PREDICTION_DEBUG
		cout << __func__ << ": initFlag is false" << endl;
#endif
		return -1;
	}

#ifdef ULC_PREDICTION_DEBUG
	cout << __func__ << ":Red Balls Pool - " << redPools[0] << endl;
#endif

	serial.sortedBalls.reds.clear();

	for (int i = 0; i < UL_RED_BALLS_COUNT; i++){
		number = redPools[i].GetNumber(); 

		if (number == -1){
			number = VSDL_Tool::Rand() % UL_BALL_MAX_RED + 1;
		}

		while (serial.actualOutBalls.IsRedBallExisted(number)){
			number = redPools[i].GetNumber();

			if (number == -1){
				number = VSDL_Tool::Rand() % UL_BALL_MAX_RED + 1;
#ifdef ULC_PREDICTION_DEBUG
				cout << __func__ << "Using Rand() \% UL_BALL_MAX_RED = " << number <<endl;
#endif
			}
#ifdef ULC_PREDICTION_DEBUG
			else{
				cout << __func__ << ":pool[" << i << "].GetNumber() = " << number << endl;
			}
#endif
		}

		serial.actualOutBalls.reds[i] = number;
	}

	serial.sortedBalls = serial.actualOutBalls;
	serial.sortedBalls.blue = bluePool.GetNumber();
	serial.sortedBalls.EnableOutputBlue();
	serial.sortedBalls.SortRedBalls();

	return 0;
}

int ULC_Prediction::PredictNextSerialByOut(ULC_Serial &serial)
{
	int number;

	if (initFlag == false){
#ifdef ULC_PREDICTION_DEBUG
		cout << __func__ << ": initFlag is false" << endl;
#endif
		return -1;
	}

	serial.sortedBalls.reds.clear();

	for (int i = 0; i < UL_RED_BALLS_COUNT; i++){
		number = outRedPools[i].GetNumber(); 

		if (number == -1){
			number = VSDL_Tool::Rand() % UL_BALL_MAX_RED + 1;
		}

		while (serial.actualOutBalls.IsRedBallExisted(number)){
			number = outRedPools[i].GetNumber();
			
			if (number == -1){
				number = VSDL_Tool::Rand() % UL_BALL_MAX_RED + 1;
			}
		}

		serial.actualOutBalls.reds[i] = number;
	}

	serial.sortedBalls = serial.actualOutBalls;
	serial.sortedBalls.blue = bluePool.GetNumber();
	serial.sortedBalls.EnableOutputBlue();
	serial.sortedBalls.SortRedBalls();

	return 0;
}

//Prediction rule:
//1. Pick the latest serial
//2. Generate statistic using "serials"
//3. Blue number will be the one with lowest rate(different from the latest serial's blue one) or the one never showed
//4. Red numbers will pick randomly from "serials" 
#ifndef MIN_SERIALS
#define MIN_SERIALS 16
#endif
int ULC_Prediction::PredictNextSerialByLatestNSerials(ULC_Serial &serial, int nSerials, ULC_Serial *serials)
{
	ULC_Serial *latestSerial;
	ULC_Statistic latestStat;
	ULC_PositionInfo *info;
	list<ULC_BallFrequency>::iterator iter;
	list<ULC_BallFrequency> tmpList;
	ULC_RandomPool neverShowedBlues;
	int count = 0;

	if (nSerials < MIN_SERIALS || serials == NULL){
		return -1;
	}

	serial.sortedBalls.reds.clear();
	latestSerial = serials + (nSerials - 1);
#ifdef ULC_PREDICTION_DEBUG
	cout << __func__ << ":Parsing " << nSerials << " serials" << endl;
#endif
	latestStat.ParseSerials(nSerials, serials);

	//Deal with blue	
	info = latestStat.GetBlueInfo();

	if (info->numberOrder.size() == UL_BALL_MAX_BLUE){
		//All blue numbers existed, using lowest rate table
#ifdef ULC_PREDICTION_DEBUG
		cout << __func__ << ": All blue numbers exist" << endl;
#endif
			count = TOP_N_BLUE_NUMBERS;

			info->GetLowestRates(tmpList, count);
pick_again:
			if (tmpList.size() == 0){
				//Basically impossible, but keep this any way
				serial.actualOutBalls.blue = VSDL_Tool::Rand() % UL_BALL_MAX_BLUE + 1;
			}else{

				iter = tmpList.begin();

				count = VSDL_Tool::Rand() % tmpList.size();

				while (count > 0){
					count--;
					iter++;
				}

				if ((*iter).number != latestSerial->sortedBalls.blue){
					serial.actualOutBalls.blue = (*iter).number;
				}else{
					tmpList.remove((*iter));
					goto pick_again;
				}
			}

	}else{
#ifdef ULC_PREDICTION_DEBUG
		cout << __func__ << ": Some of the blue numbers hasn't shown " << endl;
#endif
		int showedBlues[UL_BALL_MAX_BLUE + 1] = {0};

		iter = info->numberOrder.begin();

		while (iter != info->numberOrder.end()){
			showedBlues[(*iter).number] = 1;
			iter++;
		}

		for (int i = 1; i <= UL_BALL_MAX_BLUE; i++){
			if (showedBlues[i] == 0){
				neverShowedBlues.AddNumber(i);
				count++;
			}
		}

#ifdef ULC_PREDICTION_DEBUG
		cout << "Blue numbers never showed in " << nSerials << " serials: " << neverShowedBlues << endl;
#endif
		serial.actualOutBalls.blue = neverShowedBlues.GetNumber();
	}

#ifdef ULC_PREDICTION_DEBUG
	cout << __func__ << ":Now deal with red ones" << endl;
#endif
	//Deal with red ones
	for (int i = 0; i < UL_RED_BALLS_COUNT; i++){
		int number = 0;
		int index;

		info = latestStat.GetSortedRedInfo(i);
		count = TOP_N_RED_NUMBERS;

		info->GetHighestRates(tmpList, count);

		index = VSDL_Tool::Rand() % nSerials;

		number = serials[index].sortedBalls.reds[i];

		while (serial.actualOutBalls.IsRedBallExisted(number)){

			count = VSDL_Tool::Rand() % tmpList.size();

			iter = tmpList.begin();

			while (count > 0){
				count--;
				iter++;
			}

			number = (*iter).number;
		}

		serial.actualOutBalls.reds[i] = number;
	}

	serial.sortedBalls = serial.actualOutBalls;
	serial.sortedBalls.EnableOutputBlue();
	serial.sortedBalls.SortRedBalls();
	return 0;
}

int ULC_Prediction::PredictNextSerial(ULC_Serial &serial)
{
	//Not implemented for now

	return 0;
}

int ULC_Prediction::PredictNextSerial(ULC_Serial &serial, int nSerials, ULC_Serial *serials)
{
	//Not implemented for now
	

	return 0;
}

int ULC_Prediction::PredictNextSerial(ULC_Serial &serial, ULC_History &historyData)
{
	//Not implemented for now

	return 0;
}
